package com.ssy.lingxi.product.serviceImpl.material;

import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.product.api.model.response.SimpleBrandResponse;
import com.ssy.lingxi.product.api.model.response.SimpleCategoryResponse;
import com.ssy.lingxi.product.api.model.response.SimpleCustomerCategoryResponse;
import com.ssy.lingxi.product.common.enums.MaterialProcessEnum;
import com.ssy.lingxi.product.common.enums.MaterialProcessStatusEnum;
import com.ssy.lingxi.product.common.enums.MaterialProcessSuitableTypeEnum;
import com.ssy.lingxi.product.common.enums.MaterialProcessTypeEnum;
import com.ssy.lingxi.product.entity.Goods;
import com.ssy.lingxi.product.entity.material.BaseMaterialProcessDO;
import com.ssy.lingxi.product.entity.material.MaterialGroup;
import com.ssy.lingxi.product.entity.material.MaterialProcessDO;
import com.ssy.lingxi.product.entity.platform.PlatformMaterialProcessDO;
import com.ssy.lingxi.product.entity.platform.PlatformMaterialProcessMemberDO;
import com.ssy.lingxi.product.model.dto.material.MaterialProcessDTO;
import com.ssy.lingxi.product.model.request.material.*;
import com.ssy.lingxi.product.model.resp.material.MaterialProcessDetailVO;
import com.ssy.lingxi.product.model.resp.material.MaterialProcessPageQueryVO;
import com.ssy.lingxi.product.model.resp.material.MaterialProcessRelMaterialGroupQueryVO;
import com.ssy.lingxi.product.model.resp.material.MaterialProcessRelMaterialQueryVO;
import com.ssy.lingxi.product.model.resp.platform.PlatformBaseMaterialProcessVO;
import com.ssy.lingxi.product.repository.GoodsRepository;
import com.ssy.lingxi.product.repository.material.BaseMaterialProcessRepository;
import com.ssy.lingxi.product.repository.material.MaterialProcessRepository;
import com.ssy.lingxi.product.repository.platform.PlatformMaterialProcessRepository;
import com.ssy.lingxi.product.service.material.IMaterialProcessService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 物料流程配置 服务接口实现
 * @author 彭冬冬
 * @version 2.0.0
 * @date 2022/3/25 14:22
 */
@Service
public class MaterialProcessServiceImpl implements IMaterialProcessService {

    @Resource
    private MaterialProcessRepository repository;

    @Resource
    private PlatformMaterialProcessRepository platformMaterialProcessRepository;

    @Resource
    private BaseMaterialProcessRepository baseMaterialProcessRepository;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private GoodsRepository goodsRepository;

    @Override
    public Wrapper<PageData<MaterialProcessPageQueryVO>> pageMaterialProcesses(UserLoginCacheDTO sysUser, MaterialProcessPageVO pageVO) {
        // step 1:组装查询条件
        Pageable pageable = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("createTime").descending());
        Specification<MaterialProcessDO> spec = (root, query, builder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()));
            list.add(builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()));
            if (StringUtils.hasText(pageVO.getName())) {
                list.add(builder.like(root.get("name").as(String.class), "%".concat(pageVO.getName()).concat("%")));
            }
            return query.where(list.toArray(new Predicate[0])).getRestriction();
        };
        // step 2:查询数据库
        Page<MaterialProcessDO> pageData = repository.findAll(spec, pageable);
        // step 3: 组装数据
        return Wrapper.success(new PageData<>(pageData.getTotalElements(), pageData.getContent().stream().map(this::builderMaterialProcessPageQueryVO).collect(Collectors.toList())));
    }

    private MaterialProcessPageQueryVO builderMaterialProcessPageQueryVO(MaterialProcessDO entity) {
        MaterialProcessPageQueryVO target = new MaterialProcessPageQueryVO();
        target.setProcessId(entity.getId());
        target.setName(entity.getName());
        target.setProcessName(MaterialProcessEnum.getNameByCode(entity.getProcessType()));
        target.setStatus(entity.getStatus());
        target.setStatusName(MaterialProcessStatusEnum.getMessage(entity.getStatus()));
        target.setCreateTime(entity.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        return target;
    }

    @Override
    public Wrapper<List<PlatformBaseMaterialProcessVO>> listBaseMaterialProcesses(UserLoginCacheDTO sysUser) {
        Long memberId = sysUser.getMemberId();
        Long roleId = sysUser.getMemberRoleId();
        Specification<PlatformMaterialProcessDO> spec = (root, query, builder) -> {
            Predicate field1 = builder.equal(root.get("allMembers").as(Boolean.class), true);
            Join<PlatformMaterialProcessDO, PlatformMaterialProcessMemberDO> members = root.join("members", JoinType.LEFT);
            Predicate field2 = builder.equal(members.get("memberId").as(Long.class), memberId);
            Predicate field3 = builder.equal(members.get("roleId").as(Long.class), roleId);
            return query.where(builder.or(field1, builder.and(field2, field3))).getRestriction();
        };
        List<PlatformMaterialProcessDO> list = platformMaterialProcessRepository.findAll(spec);
        return Wrapper.success(list.stream().filter(f -> Objects.equals(f.getStatus(), MaterialProcessStatusEnum.ENABLE.getCode())).map(PlatformMaterialProcessDO::getProcess).sorted(Comparator.comparingLong(BaseMaterialProcessDO::getId)).map(this::builderPlatformBaseMaterialProcessVO).collect(Collectors.toList()));
    }

    private PlatformBaseMaterialProcessVO builderPlatformBaseMaterialProcessVO(BaseMaterialProcessDO baseMaterialProcess) {
        PlatformBaseMaterialProcessVO target = new PlatformBaseMaterialProcessVO();
        target.setBaseProcessId(baseMaterialProcess.getId());
        target.setProcessName(baseMaterialProcess.getName());
        target.setProcessType(baseMaterialProcess.getProcessType());
        target.setProcessTypeName(MaterialProcessTypeEnum.getMessage(baseMaterialProcess.getProcessType()));
        target.setDescription(MaterialProcessEnum.getRemarkByCode(baseMaterialProcess.getCode()));
        return target;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> save(UserLoginCacheDTO sysUser, MaterialProcessVO saveVO) {
        // step 1: 新增校验
        Wrapper<BaseMaterialProcessDO> checkWrapper = checkSave(sysUser, saveVO);
        if (checkWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(checkWrapper);
        }
        // step 2: 组装数据
        MaterialProcessDO entity = builderEntity(sysUser, checkWrapper.getData(), saveVO);
        // step 3: 保存数据库
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    private MaterialProcessDO builderEntity(UserLoginCacheDTO sysUser,BaseMaterialProcessDO baseMaterialProcess, MaterialProcessVO saveVO) {
        MaterialProcessDO entity = new MaterialProcessDO();
        entity.setCreateTime(LocalDateTime.now());
        entity.setMemberId(sysUser.getMemberId());
        entity.setRoleId(sysUser.getMemberRoleId());
        entity.setName(saveVO.getName());
        entity.setBaseMaterialProcess(baseMaterialProcess);
        entity.setProcessType(baseMaterialProcess.getProcessType());
        entity.setProcessKey(baseMaterialProcess.getProcessKey());
        entity.setSuitableMaterialType(saveVO.getSuitableMaterialType());
        entity.setStatus(MaterialProcessStatusEnum.ENABLE.getCode());
        List<MaterialGroup> materialGroups = builderMaterialGroups(saveVO);
        entity.setMaterialGroups(materialGroups);
        List<Goods> materials = builderMaterials(saveVO);
        entity.setMaterials(materials);
        return entity;
    }

    private List<Goods> builderMaterials(MaterialProcessVO saveVO) {
        return !Objects.equals(MaterialProcessSuitableTypeEnum.MATERIAL.getCode(), saveVO.getSuitableMaterialType()) || CollectionUtils.isEmpty(saveVO.getMaterials())
                ? new ArrayList<>()
                : saveVO.getMaterials().stream().map(map -> {
                    Goods target = new Goods();
                    target.setId(map.getMaterialId());
                    return target;
                }).collect(Collectors.toList());
    }

    private List<MaterialGroup> builderMaterialGroups(MaterialProcessVO saveVO) {
        return !Objects.equals(MaterialProcessSuitableTypeEnum.GROUP.getCode(), saveVO.getSuitableMaterialType()) || CollectionUtils.isEmpty(saveVO.getMaterialGroups())
                ? new ArrayList<>()
                : saveVO.getMaterialGroups().stream().map(map -> {
                    MaterialGroup target = new MaterialGroup();
                    target.setId(map.getMaterialGroupId());
                    return target;
                }).collect(Collectors.toList());
    }

    private Wrapper<BaseMaterialProcessDO> checkSave(UserLoginCacheDTO sysUser, MaterialProcessVO saveVO) {
        // step 1: 判断基础流程
        BaseMaterialProcessDO baseMaterialProcess = baseMaterialProcessRepository.findById(saveVO.getBaseProcessId()).orElse(null);
        if (Objects.isNull(baseMaterialProcess)) {
            return Wrapper.fail(ResponseCode.PRODUCT_BASE_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        // step 2: 判断适用物料类型
        MaterialProcessSuitableTypeEnum suitableMaterialType = MaterialProcessSuitableTypeEnum.getEnum(saveVO.getSuitableMaterialType());
        Specification<MaterialProcessDO> spec;
        switch (suitableMaterialType) {
            // 适用部分物料
            case MATERIAL:
                // 必须选取一个物料id
                if (CollectionUtils.isEmpty(saveVO.getMaterials())) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_IS_EMPTY);
                }
                // 查询数据库中是适用类型是部分物料，且改物料已选取
                spec = (root, query, builder) -> {
                    Join<MaterialProcessDO, Goods> materials = root.join("materials", JoinType.INNER);
                    CriteriaBuilder.In<Long> materialIdIn = builder.in(materials.get("id").as(Long.class));
                    saveVO.getMaterials().stream().map(MaterialProcessRelMaterialVO::getMaterialId).forEach(materialIdIn::value);
                    return query.where(builder.equal(root.get("suitableMaterialType").as(Integer.class), MaterialProcessSuitableTypeEnum.MATERIAL.getCode()),
                            builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()),
                            builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()),
                            builder.equal(root.get("processType").as(Integer.class), baseMaterialProcess.getProcessType()),
                            materialIdIn).getRestriction();
                };
                List<MaterialProcessDO> entities1 = repository.findAll(spec);
                if (!CollectionUtils.isEmpty(entities1)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_EXIST);
                }
                break;
            // 适用部分物料组
            case GROUP:
                // 必须选取一个物料分组id
                if (CollectionUtils.isEmpty(saveVO.getMaterialGroups())) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_GROUP_IS_EMPTY);
                }
                // 查询数据库中是适用类型是部分物料，且改物料已选取
                spec = (root, query, builder) -> {
                    Join<MaterialProcessDO, MaterialGroup> materialGroups = root.join("materialGroups", JoinType.INNER);
                    CriteriaBuilder.In<Long> materialIdIn = builder.in(materialGroups.get("id").as(Long.class));
                    saveVO.getMaterialGroups().stream().map(MaterialProcessRelMaterialGroupVO::getMaterialGroupId).forEach(materialIdIn::value);
                    return query.where(builder.equal(root.get("suitableMaterialType").as(Integer.class), MaterialProcessSuitableTypeEnum.GROUP.getCode()),
                            builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()),
                            builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()),
                            builder.equal(root.get("processType").as(Integer.class), baseMaterialProcess.getProcessType()),
                            materialIdIn).getRestriction();
                };
                List<MaterialProcessDO> entities2 = repository.findAll(spec);
                if (!CollectionUtils.isEmpty(entities2)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_GROUP_EXIST);
                }
                break;
            // 适用所有物料
            case ALL:
                spec = (root, query, builder) -> query.where(builder.equal(root.get("suitableMaterialType").as(Integer.class), MaterialProcessSuitableTypeEnum.ALL.getCode()),builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()),builder.equal(root.get("processType").as(Integer.class), baseMaterialProcess.getProcessType()),builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId())).getRestriction();
                List<MaterialProcessDO> entities3 = repository.findAll(spec);
                if (!CollectionUtils.isEmpty(entities3)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_all_MATERIAL_EXIST);
                }
                break;
            default:
                return Wrapper.fail(ResponseCode.PRODUCT_SUITABLE_MATERIAL_TYPE_UNKNOWN);
        }
        return Wrapper.success(baseMaterialProcess);
    }

    @Override
    public Wrapper<MaterialProcessDetailVO> getInfo(UserLoginCacheDTO sysUser, Long processId) {
        MaterialProcessDO entity = repository.findById(processId).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        MaterialProcessDetailVO target = new MaterialProcessDetailVO();
        target.setProcessId(entity.getId());
        BaseMaterialProcessDO baseMaterialProcess = entity.getBaseMaterialProcess();
        target.setBaseProcessId(baseMaterialProcess.getId());
        PlatformBaseMaterialProcessVO baseProcess = builderPlatformBaseMaterialProcessVO(baseMaterialProcess);
        target.setBaseProcess(baseProcess);
        target.setProcessType(baseProcess.getProcessType());
        target.setProcessTypeName(baseProcess.getProcessTypeName());
        target.setName(entity.getName());
        target.setSuitableMaterialType(entity.getSuitableMaterialType());
        target.setSuitableMaterialTypeName(MaterialProcessSuitableTypeEnum.getMessage(entity.getSuitableMaterialType()));
        target.setStatus(entity.getStatus());
        target.setStatusName(MaterialProcessStatusEnum.getMessage(entity.getStatus()));
        return Wrapper.success(target);
    }

    @Override
    public Wrapper<List<MaterialProcessRelMaterialGroupQueryVO>> treeRelMaterialGroup(UserLoginCacheDTO sysUser, Long processId) {
        // step 1:查询物料流程
        MaterialProcessDO entity = repository.findById(processId).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        List<MaterialGroup> materialGroups = entity.getMaterialGroups();
        // step 2:查询物料组数结构
        // step 3:构建数据
        List<MaterialProcessRelMaterialGroupQueryVO> target = builderRelMaterialGroups( materialGroups);
        return Wrapper.success(target);
    }

    private List<MaterialProcessRelMaterialGroupQueryVO> builderRelMaterialGroups( List<MaterialGroup> materialGroups) {
        return CollectionUtils.isEmpty(materialGroups)
                ? new ArrayList<>()
                :materialGroups.stream().map(map -> {
                    MaterialProcessRelMaterialGroupQueryVO item = new MaterialProcessRelMaterialGroupQueryVO();
                    item.setMaterialGroupId(map.getId());
                    item.setMaterialGroupName(map.getName());
                    return item;
                }).collect(Collectors.toList());
    }

    @Override
    public Wrapper<List<MaterialProcessRelMaterialQueryVO>> listRelMaterial(UserLoginCacheDTO sysUser, MaterialProcessRelMaterialPageVO pageVO) {
        // step 1:查询物料流程
        MaterialProcessDO entity = repository.findById(pageVO.getProcessId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        // step 2:查询关联匹配的物料信息
        List<Goods> materials = entity.getMaterials();
        if (CollectionUtils.isEmpty(materials)) {
            return Wrapper.success(new ArrayList<>());
        }
        List<Long> materialIds = materials.stream().map(Goods::getId).collect(Collectors.toList());
          Specification<Goods> spec = (root, query, builder) -> {
            List<Predicate> list = new ArrayList<>();
            CriteriaBuilder.In<Long> in = builder.in(root.get("id").as(Long.class));
            materialIds.forEach(in::value);
            list.add(in);
            if (StringUtils.hasText(pageVO.getMaterialCode())) {
                list.add(builder.like(root.get("code").as(String.class), "%".concat(pageVO.getMaterialName().concat("%"))));
            }
            if (StringUtils.hasText(pageVO.getMaterialName())) {
                list.add(builder.like(root.get("name").as(String.class), "%".concat(pageVO.getMaterialName().concat("%"))));
            }
            return query.where(list.toArray(new Predicate[0])).getRestriction();
        };
        List<Goods> listData = goodsRepository.findAll(spec);
        return Wrapper.success(listData.stream().map(map -> {
            MaterialProcessRelMaterialQueryVO item = new MaterialProcessRelMaterialQueryVO();
            item.setMaterialId(map.getId());
            item.setMaterialCode(map.getCode());
            item.setMaterialName(map.getName());
            item.setMaterialGroupId(Optional.ofNullable(map.getMaterialGroup()).map(MaterialGroup::getId).orElse(0L));
            item.setMaterialGroupName(Optional.ofNullable(map.getMaterialGroup()).map(MaterialGroup::getName).orElse(""));
            item.setType(map.getType());

            Optional.ofNullable(map.getCustomerCategory()).ifPresent(p -> {
                SimpleCustomerCategoryResponse simpleCustomerCategoryResponse = new SimpleCustomerCategoryResponse();
                simpleCustomerCategoryResponse.setId(p.getId());
                simpleCustomerCategoryResponse.setName(p.getName());
                simpleCustomerCategoryResponse.setFullId(p.getFullId());
                simpleCustomerCategoryResponse.setSort(p.getSort());
                Optional.ofNullable(p.getCategory()).ifPresent(p2 -> {
                    SimpleCategoryResponse simpleCategoryResponse = new SimpleCategoryResponse();
                    simpleCategoryResponse.setId(p2.getId());
                    simpleCategoryResponse.setName(p2.getName());
                    simpleCategoryResponse.setFullId(p2.getFullId());
                    simpleCustomerCategoryResponse.setCategory(simpleCategoryResponse);
                });
                item.setCustomerCategory(simpleCustomerCategoryResponse);
            });

            Optional.ofNullable(map.getBrand()).ifPresent(p -> {
                SimpleBrandResponse simpleBrandResponse = new SimpleBrandResponse();
                simpleBrandResponse.setId(p.getId());
                simpleBrandResponse.setName(p.getName());
                simpleBrandResponse.setLogoUrl(p.getLogoUrl());
                item.setBrand(simpleBrandResponse);
            });

            item.setUnitId(map.getUnitId());
            String unit = redisStringUtils.hMGet(Constants.REDIS_KEY_UNIT, String.valueOf(map.getUnitId()), Constants.REDIS_PLATFORM_MANAGE_INDEX);
            item.setUnitName(org.apache.commons.lang.StringUtils.isNotEmpty(unit) ? unit.split(Constants.TRANSFER_STR + Constants.SPLIT_STR)[0] : "");

            return item;
        }).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> update(UserLoginCacheDTO sysUser, MaterialProcessUpdateVO updateVO) {
        // step 1:校验
        Wrapper<MaterialProcessDO> updateWrapper = checkUpdate(sysUser, updateVO);
        if (updateWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(updateWrapper);
        }
        // step 2:组装数据
        MaterialProcessDO entity = updateWrapper.getData();
        entity.setCreateTime(LocalDateTime.now());
        entity.setMemberId(sysUser.getMemberId());
        entity.setRoleId(sysUser.getMemberRoleId());
        entity.setName(updateVO.getName());
        entity.setProcessType(entity.getBaseMaterialProcess().getProcessType());
        entity.setProcessKey(entity.getBaseMaterialProcess().getProcessKey());
        entity.setSuitableMaterialType(updateVO.getSuitableMaterialType());
        entity.setStatus(entity.getStatus());
        entity.setMaterialGroups(builderMaterialGroups(updateVO));
        entity.setMaterials(builderMaterials(updateVO));

        // step 3:修改数据库
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }


    private Wrapper<MaterialProcessDO> checkUpdate(UserLoginCacheDTO sysUser, MaterialProcessUpdateVO updateVO) {
        // step 1: 查询物料流程是否存在
        MaterialProcessDO entity = repository.findById(updateVO.getProcessId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        if(Objects.equals(MaterialProcessStatusEnum.ENABLE.getCode(), entity.getStatus())) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_NOT_UPDATE_PERMISSION);
        }
        // step 2: 查询基础流程是否存在
        BaseMaterialProcessDO baseMaterialProcess = baseMaterialProcessRepository.findById(updateVO.getBaseProcessId()).orElse(null);
        if (Objects.isNull(baseMaterialProcess)) {
            return Wrapper.fail(ResponseCode.PRODUCT_BASE_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        // step 3: 判断适用物料类型
        MaterialProcessSuitableTypeEnum suitableMaterialType = MaterialProcessSuitableTypeEnum.getEnum(updateVO.getSuitableMaterialType());
        Specification<MaterialProcessDO> spec;
        switch (suitableMaterialType) {
            // 适用部分物料
            case MATERIAL:
                // 必须选取一个物料id
                if (CollectionUtils.isEmpty(updateVO.getMaterials())) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_IS_EMPTY);
                }
                // 查询数据库中是适用类型是部分物料，且改物料已选取
                spec = (root, query, builder) -> {
                    Join<MaterialProcessDO, Goods> materials = root.join("materials", JoinType.INNER);
                    CriteriaBuilder.In<Long> materialIdIn = builder.in(materials.get("id").as(Long.class));
                    updateVO.getMaterials().stream().map(MaterialProcessRelMaterialVO::getMaterialId).forEach(materialIdIn::value);
                    return query.where(builder.equal(root.get("suitableMaterialType").as(Integer.class), MaterialProcessSuitableTypeEnum.MATERIAL.getCode()),
                            builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()),
                            builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()),
                            builder.notEqual(root.get("id").as(Long.class), updateVO.getProcessId()),
                            builder.equal(root.get("processType").as(Integer.class), baseMaterialProcess.getProcessType()),
                            materialIdIn).getRestriction();
                };
                List<MaterialProcessDO> entities1 = repository.findAll(spec);
                if (!CollectionUtils.isEmpty(entities1)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_EXIST);
                }
                break;
            // 适用部分物料组
            case GROUP:
                // 必须选取一个物料分组id
                if (CollectionUtils.isEmpty(updateVO.getMaterialGroups())) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_GROUP_IS_EMPTY);
                }
                // 查询数据库中是适用类型是部分物料，且改物料已选取
                spec = (root, query, builder) -> {
                    Join<MaterialProcessDO, MaterialGroup> materialGroups = root.join("materialGroups", JoinType.INNER);
                    CriteriaBuilder.In<Long> materialIdIn = builder.in(materialGroups.get("id").as(Long.class));
                    updateVO.getMaterialGroups().stream().map(MaterialProcessRelMaterialGroupVO::getMaterialGroupId).forEach(materialIdIn::value);
                    return query.where(builder.equal(root.get("suitableMaterialType").as(Integer.class), MaterialProcessSuitableTypeEnum.GROUP.getCode()),
                            builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()),
                            builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()),
                            builder.notEqual(root.get("id").as(Long.class), updateVO.getProcessId()),
                            builder.equal(root.get("processType").as(Integer.class), baseMaterialProcess.getProcessType()),
                            materialIdIn).getRestriction();
                };
                List<MaterialProcessDO> entities2 = repository.findAll(spec);
                if (!CollectionUtils.isEmpty(entities2)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_MATERIAL_GROUP_EXIST);
                }
                break;
            // 适用所有物料
            case ALL:
                spec = (root, query, builder) -> query.where(builder.equal(root.get("suitableMaterialType").as(Integer.class), MaterialProcessSuitableTypeEnum.ALL.getCode()),                             builder.equal(root.get("processType").as(Integer.class), baseMaterialProcess.getProcessType()), builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()),builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()),builder.notEqual(root.get("id").as(Long.class), updateVO.getProcessId())).getRestriction();
                List<MaterialProcessDO> entities3 = repository.findAll(spec);
                if (!CollectionUtils.isEmpty(entities3)) {
                    return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_REL_all_MATERIAL_EXIST);
                }
                break;
            default:
                return Wrapper.fail(ResponseCode.PRODUCT_SUITABLE_MATERIAL_TYPE_UNKNOWN);
        }
        entity.setBaseMaterialProcess(baseMaterialProcess);
        return Wrapper.success(entity);
    }

    @Override
    public Wrapper<Void> updateStatus(UserLoginCacheDTO sysUser, MaterialProcessUpdateStatusVO updateStatusVO) {
        // step 1: 查询物料流程是否存在
        MaterialProcessDO entity = repository.findById(updateStatusVO.getProcessId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        entity.setStatus(updateStatusVO.getStatus());
        entity.setCreateTime(LocalDateTime.now());
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    @Override
    public Wrapper<Void> delete(UserLoginCacheDTO sysUser, Long processId) {
        MaterialProcessDO entity = repository.findById(processId).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
        }
        if(Objects.equals(MaterialProcessStatusEnum.ENABLE.getCode(), entity.getStatus())) {
            return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_NOT_DELETE_PERMISSION);
        }
        repository.delete(entity);
        return Wrapper.success();
    }

    @Override
    public Wrapper<Boolean> isExistMaterialProcess(UserLoginCacheDTO sysUser, MaterialProcessRelVO queryVO) {
        Wrapper<MaterialProcessDTO> wrapper = getMaterialProcessByMemberIdAndRoleIdAndMaterialGroupIdAndMaterialIdAndProcessType(sysUser.getMemberId(), sysUser.getMemberRoleId(), queryVO.getMaterialGroupId(), queryVO.getMaterialId(), queryVO.getProcessType());
        if (wrapper.getCode() != ResponseCode.SUCCESS.getCode() || Optional.ofNullable(wrapper.getData()).map(MaterialProcessDTO::getProcessKey).map(map -> Objects.equals(map, "")).orElse(true)) {
            return Wrapper.success(false);
        }
        return Wrapper.success(true);
    }

    @Override
    public Wrapper<MaterialProcessDTO> getMaterialProcessByMemberIdAndRoleIdAndMaterialGroupIdAndMaterialIdAndProcessType(Long memberId, Long roleId, Long materialGroupId, Long materialId, Integer processType) {
        // step 1:查询所有符合会员id、角色id、流程类型的物料流程规则配置
        Specification<MaterialProcessDO> spec = (root, query, builder) -> query.where(builder.equal(root.get("memberId").as(Long.class), memberId), builder.equal(root.get("roleId").as(Long.class),roleId), builder.equal(root.get("processType").as(Integer.class), processType)).getRestriction();
        List<MaterialProcessDO> list = repository.findAll(spec);
        MaterialProcessDTO target;
        // 物料流程规则配置，适用物料、存在id值的
        if(list.stream().filter(f -> Objects.equals(MaterialProcessSuitableTypeEnum.MATERIAL.getCode(), f.getSuitableMaterialType())).flatMap(map -> map.getMaterials().stream()).map(Goods::getId).anyMatch(f -> Objects.equals(materialId, f))) {
            MaterialProcessDO entity = list.stream().filter(f -> Objects.equals(MaterialProcessSuitableTypeEnum.MATERIAL.getCode(), f.getSuitableMaterialType()) && f.getMaterials().stream().map(Goods::getId).anyMatch(f2 -> Objects.equals(materialId, f2))).findFirst().orElse(null);
            if (Objects.nonNull(entity)) {
                target = new MaterialProcessDTO();
                target.setProcessKey(entity.getProcessKey());
                return Wrapper.success(target);
            }
        }
        // 物料流程规则配置，适用物料分组、存在id值的
        if (list.stream().filter(f -> Objects.equals(MaterialProcessSuitableTypeEnum.GROUP.getCode(), f.getSuitableMaterialType())).flatMap(map -> map.getMaterialGroups().stream()).map(MaterialGroup::getId).anyMatch(f -> Objects.equals(materialGroupId, f))) {
            MaterialProcessDO entity = list.stream().filter(f -> Objects.equals(MaterialProcessSuitableTypeEnum.GROUP.getCode(), f.getSuitableMaterialType()) && f.getMaterialGroups().stream().map(MaterialGroup::getId).anyMatch(f2 -> Objects.equals(materialGroupId, f2))).findFirst().orElse(null);
            if (Objects.nonNull(entity)) {
                target = new MaterialProcessDTO();
                target.setProcessKey(entity.getProcessKey());
                return Wrapper.success(target);
            }
        }
        if (list.stream().anyMatch(f -> Objects.equals(MaterialProcessSuitableTypeEnum.ALL.getCode(),f.getSuitableMaterialType()))) {
            MaterialProcessDO entity = list.stream().filter(f -> Objects.equals(MaterialProcessSuitableTypeEnum.ALL.getCode(),f.getSuitableMaterialType())).findFirst().orElse(null);
            if (Objects.nonNull(entity)) {
                target = new MaterialProcessDTO();
                target.setProcessKey(entity.getProcessKey());
                return Wrapper.success(target);
            }
        }
        return Wrapper.fail(ResponseCode.PRODUCT_MATERIAL_PROCESS_DOES_NOT_EXIST);
    }
}
